package manager

import (
	"context"
	"errors"
	"fmt"
	"github.com/tianjigames/fairy/constants"
	"github.com/tianjigames/fairy/dao"
	"github.com/tianjigames/fairy/pojo"
	protos2 "github.com/tianjigames/fairy/protos"
	"github.com/tianjigames/fairy/util"
	"github.com/topfreegames/pitaya"
	"github.com/topfreegames/pitaya/logger"
	"sort"
)

type SortDataList []*pojo.StandData
func (p SortDataList) Len() int           { return len(p) }
//return true i 在前 j在后
func (p SortDataList) Less(i, j int) bool { return p[i].SRank < p[j].SRank }
func (p SortDataList) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

type (
	StandLogic interface {
		GetMaxNum() int
		Init(p StandLogic) error
		InsertStand(logic StandLogic ,param interface{}) (*pojo.StandData,error) //插入排名
		GetLogicId() int
		IsAfter(before *pojo.StandData,msgData *protos2.MsgStandData) bool //新数据在老数据排名之后
		CanInsert(oldVal,newVal int) bool
		IsInForbid(guid int64) bool
		UpdateStand(param interface{}) error
		UpdateMsg() error
	}

	BaseStandLogic struct {
		SortDatas SortDataList
		ListMsgDatas []*protos2.MsgStandData
	}
)

func NewBaseStandLogic() *BaseStandLogic {
	return &BaseStandLogic{
		SortDatas: make([]*pojo.StandData,0),
		ListMsgDatas: make([]*protos2.MsgStandData,0),
	}
}

func (p *BaseStandLogic) GetMaxNum() int {
	return MaxStandNum
}

func (base *BaseStandLogic) Init(p StandLogic) error {
	result := make([]*pojo.StandData,0)
	err := dao.StandDataDao.GetListByDb(&pojo.StandData{
		SType:p.GetLogicId(),
	},&result)
	if err != nil {
		logger.Log.Errorf("[BaseStandLogic] Init failed,stype=%d,error:%s",p.GetLogicId(),err.Error())
		return err
	}

	if result == nil || len(result) == 0 {
		return nil
	}

	for _,v := range result {
		gangId := v.GangId
		if gangId > 0 {
			gangData := GangServerManager.GetGangById(gangId)
			if gangData != nil {
				v.GangName = gangData.GangName
			}else{
				logger.Log.Errorf("[BaseStandLogic] Init failed,gang not exist,id:%d",gangId)
			}
		}
		base.SortDatas = append(base.SortDatas,v)
	}

	sort.Sort(base.SortDatas)
	return nil
}

func (base *BaseStandLogic) InsertStand(p StandLogic,param interface{}) (*pojo.StandData,error) {
	msgData,ok := param.(*protos2.MsgStandData)
	if !ok {
		logger.Log.Errorf("[BaseStandLogic] param error type:%d",p.GetLogicId())
		return nil,nil
	}

	if p.IsInForbid(msgData.Guid) {
		return nil,nil
	}

	maxNum := p.GetMaxNum()
	removeIdx := -1
	index := 0
	var standData *pojo.StandData
	for i:=0;i<base.SortDatas.Len();i++ {
		temp := base.SortDatas[i]
		if temp.SGuid == msgData.Guid {
			if !p.CanInsert(temp.SValue,int(msgData.Value)) {
				return nil,nil
			}

			if standData == nil {
				standData = temp
			}
			removeIdx = i
			continue
		}

		if p.IsAfter(temp,msgData) {
			index += 1
		}

		if index >= maxNum {
			return nil,nil
		}
	}

	if removeIdx >= 0 {
		base.SortDatas = append(base.SortDatas[:removeIdx],base.SortDatas[removeIdx+1:]...)
	}

	if standData == nil {
		standData = pojo.NewStandData(p.GetLogicId(),msgData.Guid)
		standData.State = pojo.StandDataStateInsert
	}else if standData.State == pojo.StandDataStateSaved {
		standData.State = pojo.StandDataStateUpdate
	}

	base.updateData(standData,msgData)
	standData.SValue = int(msgData.Value)
	end := append(base.SortDatas[index+1:])
	base.SortDatas = append(base.SortDatas[:index],standData)
	base.SortDatas = append(base.SortDatas,end...)

	size := base.SortDatas.Len()
	if size > maxNum {
		delData := base.SortDatas[size-1]
		base.SortDatas = append(base.SortDatas[:size-1])
		if delData != nil && delData.State != pojo.StandDataStateInsert {
		 	err := dao.StandDataDao.Delete(delData)
		 	if err != nil {
		 		logger.Log.Errorf("[BaseStandLogic] InsertStand failed,error:%s",err.Error())
		 		return nil,err
			}
		}
	}
	return standData,nil
}

func (p *BaseStandLogic) GetLogicId() int {
	return 0
}

func (p *BaseStandLogic) CanInsert(oldVal,newVal int) bool {
	return oldVal != newVal
}

func (p *BaseStandLogic) IsInForbid(guid int64) bool {
	logic := StandManager.GetStandLogic(StandLogicForbid).(*ForbidStandLogic)
	for _,data := range logic.SortDatas {
		if data.SGuid == guid && data.ExtInt == 0 {
			return true
		}
	}

	return false
}


func (p *BaseStandLogic) IsAfter(before *pojo.StandData,after *protos2.MsgStandData) bool {
	return before.SValue >= int(after.Value)
}

/**
更新数据
 */
func (p *BaseStandLogic) updateData(standData *pojo.StandData,msgData *protos2.MsgStandData) {
	needUpdate := false
	if standData.SName == "" || standData.SName != msgData.Name {
		standData.SName = msgData.Name
		needUpdate = true
	}

	if lv := int(msgData.Level);standData.SLevel != lv {
		standData.SLevel = lv
		needUpdate = true
	}

	if campId := int(msgData.CampId);standData.CampId != campId {
		standData.CampId = campId
		needUpdate = true
	}

	if careerId := int(msgData.CareerId);standData.CareerId != careerId {
		standData.CareerId = careerId
		needUpdate = true
	}

	if sexId := int(msgData.SexId);standData.SexId != sexId {
		standData.SexId = sexId
		needUpdate = true
	}

	if standData.GangId != msgData.GangId {
		standData.GangId = msgData.GangId
		needUpdate = true
	}

	if standData.GangName != msgData.GangName {
		standData.GangName = msgData.GangName
	}

	if vipLv := int(msgData.VipLv);standData.VipLevel != vipLv {
		standData.VipLevel = vipLv
		needUpdate = true
	}

	if power := int(msgData.CombatPower);standData.CombatPower != power {
		standData.CombatPower = power
		needUpdate = true
	}

	if ext := int(msgData.ExtInt);standData.ExtInt != ext {
		standData.ExtInt = ext
		needUpdate = true
	}

	if needUpdate && standData.State != pojo.StandDataStateUpdate {
		standData.State = pojo.StandDataStateUpdate
	}
}

func (p *BaseStandLogic) UpdateStand(param interface{}) error {
	msgData,ok := param.(*protos2.MsgStandData)
	if !ok {
		err := errors.New("param type parse failed")
		logger.Log.Errorf("[BaseStandLogic] UpdateStand failed,error:%s",err.Error())
		return err
	}

	for _,data := range p.SortDatas {
		if data.GetGuid() == msgData.Guid {
			p.updateData(data,msgData)
			break
		}


	}
	return nil
}

func (p *BaseStandLogic) UpdateMsg() error {
	p.ListMsgDatas = make([]*protos2.MsgStandData,0)
	for _,standData := range p.SortDatas {
		if len(p.ListMsgDatas) >= MaxStandNum {
			break
		}

		p.ListMsgDatas = append(p.ListMsgDatas,p.MakeStandMsg(standData))
	}
	return nil
}

func (p *BaseStandLogic) MakeStandMsg(standData *pojo.StandData) *protos2.MsgStandData {
	builder := &protos2.MsgStandData{
		Guid: standData.SGuid,
		Name: standData.SName,
		Value: int32(standData.SValue),
		Level: int32(standData.SLevel),
		CampId: int32(standData.CampId),
		CareerId: int32(standData.CareerId),
		SexId: int32(standData.SexId),
		GangId: standData.GangId,
		GangName: standData.GangName,
		VipLv: int32(standData.VipLevel),
		CombatPower: int32(standData.CombatPower),
	}
	return builder
}


/**
玩家战斗力排行榜
 */
type PowerStandLogic struct {
	BaseStandLogic
}

func NewPowerStandLogic() *PowerStandLogic {
	return &PowerStandLogic{
		BaseStandLogic:*NewBaseStandLogic(),
	}
}

func (p *PowerStandLogic) GetLogicId() int {
	return StandLogicPower
}

func (p *PowerStandLogic) UpdateMsg() error {
	now := util.Now()
	firstMills := util.DateUtil.GetDayFirstMills(now)
	if now - firstMills < util.HourMills {//跨天
		err := p.CheckDiffDay(now)
		if err != nil {
			logger.Log.Errorf("[PowerStandLogic] UpdateMsg failed,error:%s",err.Error())
			return err
		}
	}
	err := p.BaseStandLogic.UpdateMsg()
	if err != nil {
		logger.Log.Errorf("[PowerStandLogic] UpdateMsg failed,error:%s",err.Error())
		return err
	}
	return nil
}

/**
跨天检查 给战力值排名前3的玩家发送奖励
 */
func (p *PowerStandLogic) CheckDiffDay(now int64) error {
	firstMillis := util.DateUtil.GetDayFirstMills(now)
	sortIdx := 0
	topCount := 3
	topOnes := make([]*pojo.StandData,topCount)
	for _,standData := range p.SortDatas {
		if standData.ExtLong > firstMillis {
			continue
		}

		if sortIdx >= 0 && sortIdx < topCount {
			topOnes[sortIdx] = standData
		}

		standData.ExtLong = now
		if standData.State == pojo.StandDataStateSaved {
			standData.State = pojo.StandDataStateUpdate
		}
		sortIdx += 1
	}

	if topOnes[0] != nil {
		rt := protos2.RankType_Power
		msg := &protos2.WGRankReward{
			RankType: &rt,
			Playername: make([]string,0),
			Career: make([]int32,0),
			GangName: make([]string,0),
			GangCamp: make([]int32,0),
			SexId: make([]int32,0),
			Level: make([]int32,0),
			PlayerGuid: make([]int64,0),
		}

		for i:=0;i<topCount;i++ {
			if topOnes[i] == nil {
				continue
			}

			msg.Playername = append(msg.Playername,topOnes[i].SName)
			msg.Career = append(msg.Career,int32(topOnes[i].CareerId))
			msg.GangName = append(msg.GangName,topOnes[i].GangName)
			msg.GangCamp = append(msg.GangCamp,int32(topOnes[i].CampId))
			msg.SexId = append(msg.SexId,int32(topOnes[i].SexId))
			msg.Level = append(msg.Level,int32(topOnes[i].SLevel))
			msg.PlayerGuid = append(msg.PlayerGuid,topOnes[i].SGuid)

			commands := make([]string,0)
			commands = append(commands,fmt.Sprintf("%d",constants.RankReward))
			commands = append(commands,fmt.Sprintf("%d",int(protos2.RankType_Power)))
			commands = append(commands,fmt.Sprintf("%d",i))
			commands = append(commands,fmt.Sprintf("%d",now))

			//todo:发送可执行邮件

			servers,err := pitaya.GetServersByType(constants.SvTypeGame)
			if err != nil {
				logger.Log.Errorf("[PowerStandLogic] CheckDiffDay failed,error:%s",err.Error())
				return err
			}

			for sId,_ := range servers {
				//todo:给gameserver gamehandler添加排行处理
				err = pitaya.RPCTo(context.Background(),sId,constants.WGRankRewardRetRoute,&protos2.GWRankRewardRet{},msg)
				if err != nil {
					logger.Log.Errorf("[PowerStandLogic] CheckDiffDay failed,error:%s",err.Error())
					continue
				}
			}
		}
	}

	return nil
}

/**
法宝排名逻辑
*/
type FabaoStandLogic struct {
	BaseStandLogic
}

func NewFabaoStandLogic() *FabaoStandLogic {
	return &FabaoStandLogic{
		BaseStandLogic:*NewBaseStandLogic(),
	}
}

func (p *FabaoStandLogic) GetLogicId() int {
	return StandLogicFabao
}

/**
禁止入帮逻辑
 */
type ForbidStandLogic struct {
	BaseStandLogic
}

func NewForbidStandLogic() *ForbidStandLogic {
	return &ForbidStandLogic{
		BaseStandLogic:*NewBaseStandLogic(),
	}
}

func (p *ForbidStandLogic) GetLogicId() int {
	return StandLogicForbid
}


func (p *ForbidStandLogic) InsertStand(logic StandLogic,param interface{}) (*pojo.StandData,error) {
	return nil,nil
}

func (p *ForbidStandLogic) IsInForbid(guid int64) bool {
	return false
}




